﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using LGPeWJNM.BaseClasses;
using LGPeWJNM.DataAccessLayer;

namespace LGPeWJNM.BatchProcessBranchAndCut
{
    public partial class Form1 : Form
    {
        public InstanciaDataset Instancias { get; set; }

        public System.Threading.Thread RodaInstancia { get; set; }

        public Dictionary<IdInstancia, Resultados> resultados { get; set; }

        public bool PararExecucao { get; set; }

        public Form1()
        {
            InitializeComponent();
            RodaInstancia = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(RotinaInstancia));
            resultados = new Dictionary<IdInstancia, Resultados>();
        }

        private void abrirToolStripMenuItem_Click(object sender, EventArgs e)
        {
            switch (BnCDataset.ShowDialog())
            {
                case DialogResult.Abort:
                    break;
                case DialogResult.Cancel:
                    break;
                case DialogResult.Ignore:
                    break;
                case DialogResult.No:
                    break;
                case DialogResult.None:
                    break;
                case DialogResult.Retry:
                    break;
                case DialogResult.OK:
                case DialogResult.Yes:
                    new System.Threading.Thread(() =>
                    {
                        Instancias = new InstanciaDataset(BnCDataset.FileName);
                        PopuladgvInstancias();
                    }).Start();
                    break;
                default:
                    break;
            }
        }

        private void PopuladgvInstancias()
        {
            var ins = Instancias.Where(s => s.Value.Deposito != null);
            dgvInstancias.BeginInvoke(new MethodInvoker(() =>
            {
                dgvInstancias.DataSource = ins.Select(s => new
                {
                    s.Key.Classe,
                    s.Key.Instancia,
                    QtyVeic = s.Value.QuantidadeVeiculos,
                    CapVeic = s.Value.CapacidadeVeiculo,
                    NrDestinos = s.Value.Destinos.Count(),
                    s.Value.Otimo,
                    Demanda = s.Value.Destinos.Sum(dest => dest.Demanda)
                }).ToList();
            }));
        }

        [PreEmptive.Attributes.Feature("StartStopExecution", EventType = PreEmptive.Attributes.FeatureEventTypes.Tick)]
        private void bIniciarParar_Click(object sender, EventArgs e)
        {
            if (bIniciarParar.Text == "Inicia&r")
            {
                if (dgvInstancias.SelectedRows.Count == 0)
                    return;
                RodaInstancia.Start(new IdInstancia(dgvInstancias.SelectedRows[0].Cells[0].Value.ToString(), dgvInstancias.SelectedRows[0].Cells[1].Value.ToString()));
                bIniciarParar.Invoke(new MethodInvoker(() =>
                 {
                     bIniciarParar.Text = "Pa&rar";
                 }));
            }
            else
            {
                PararExecucao = true;
                RodaInstancia.Join(500);
                RodaInstancia.Abort();
                RodaInstancia = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(RotinaInstancia));
                bIniciarParar.Invoke(new MethodInvoker(() =>
                {
                    bIniciarParar.Text = "Inicia&r";
                }));
            }
        }

        [PreEmptive.Attributes.Feature("CvrpOptmizationProcessing", EventType = PreEmptive.Attributes.FeatureEventTypes.Tick)]
        private void RotinaInstancia(object input)
        {
            var id = (IdInstancia)input;
            var instancia = Instancias[id];

            PararExecucao = false;

            cEvolucao.Invoke(new MethodInvoker(() =>
            {
                cEvolucao.Series["IME"].Points.Clear();
                cEvolucao.Series["VMP"].Points.Clear();
                cEvolucao.Series["Ótimo"].Points.Clear();
            }));

            var tb = new TeitzEBart.TeitzEBart();
            var mc = new TeitzEBart.TeitzEBart();
            var gj = new GilletJohnson.GilletJohnson();
            var vmp = new Vmp.Vmp();
            var ime = new Ime.Ime();
            var ac = new AntColony.AntColony(1.3, 1, 0.5, 10);
            var opt = new _2Opt._2Opt();

            var iteracao = 0;
            var errCount = 0;
            while (!PararExecucao)
            {
                try
                {
                    var inicio = DateTime.Now;

                    IEnumerable<BaseClasses.Point> meds = null;
                    if (rbTeitzEBart.Checked)
                        meds = tb.Processar(instancia.Destinos, instancia.QuantidadeVeiculos, instancia.CapacidadeVeiculo);
                    else if (rbMonteCarlo.Checked)
                        meds = mc.Processar(instancia.Destinos, instancia.QuantidadeVeiculos, instancia.CapacidadeVeiculo);

                    var si = gj.Processar(meds, instancia.Destinos, instancia.Deposito, instancia.CapacidadeVeiculo);

                    var imeRoteirizacao = ime.Processar(si, instancia.Deposito, instancia.QuantidadeVeiculos, instancia.CapacidadeVeiculo);
                    var vmpRoteirizacao = vmp.Processar(si, instancia.Deposito, instancia.QuantidadeVeiculos, instancia.CapacidadeVeiculo);
                    var fimRot = DateTime.Now;

                    var imeRefino = opt.Processar(imeRoteirizacao, instancia.Deposito, instancia.QuantidadeVeiculos, instancia.CapacidadeVeiculo).AsParallel();
                    var vmpRefino = opt.Processar(vmpRoteirizacao, instancia.Deposito, instancia.QuantidadeVeiculos, instancia.CapacidadeVeiculo).AsParallel();//.AsParallel();

                    if (imeRefino.Select(s => s.Sum(su => su.Demanda)).Sum() != instancia.Destinos.AsParallel().Sum(s => s.Demanda))
                        throw new MissingMemberException("Podem haver clientes não designados, pois não foi atendida toda a demanda do problema");
                    if (vmpRefino.Select(s => s.Sum(su => su.Demanda)).Sum() != instancia.Destinos.AsParallel().Sum(s => s.Demanda))
                        throw new MissingMemberException("Podem haver clientes não designados, pois não foi atendida toda a demanda do problema");

                    var fim = DateTime.Now;
                    iteracao++;
                    errCount = 0;

                    var imeRefinoCustoTotal = imeRefino.CustoTotal();
                    var vmpRefinoCustoTotal = vmpRefino.CustoTotal();
                    var minRefinoCustoTotal = imeRefinoCustoTotal > vmpRefinoCustoTotal ? vmpRefinoCustoTotal : imeRefinoCustoTotal;

                    Resultados resultado = new Resultados()
                    {
                        Id = id,
                        imeCusto = imeRefino.CustoTotal(),
                        vmpCusto = vmpRefino.CustoTotal(),
                        TempoProcessamento = fim.Subtract(inicio).TotalMilliseconds,
                        Benchmark = instancia.Otimo,
                        Desvio = (minRefinoCustoTotal / (double)instancia.Otimo - 1) * 100,
                        SolucaoInicial = si.Select(s => (s.Cast<LGPeWJNM.BaseClasses.Cliente>()).ToArray()).ToArray(),
                        imeRoteirizacao = imeRoteirizacao.Select(s => (s.Cast<LGPeWJNM.BaseClasses.Cliente>()).ToArray()).ToArray(),
                        vmpRoteirizacao = vmpRoteirizacao.Select(s => (s.Cast<LGPeWJNM.BaseClasses.Cliente>()).ToArray()).ToArray(),
                        imeRefino = imeRefino.Select(s => (s.Cast<LGPeWJNM.BaseClasses.Cliente>()).ToArray()).ToArray(),
                        vmpRefino = vmpRefino.Select(s => (s.Cast<LGPeWJNM.BaseClasses.Cliente>()).ToArray()).ToArray()
                    };
                    ArmazenaResultado(id, resultado, iteracao);
                }
                catch (ArgumentOutOfRangeException)
                {
                    errCount++;
                    if (errCount >= 2)
                        iteracao++;
                }
                catch (MissingMemberException)
                {
                    errCount++;
                    if (errCount >= 2)
                        iteracao++;
                }
                catch (AggregateException)
                {
                    errCount++;
                    if (errCount >= 2)
                        iteracao++;
                }
                catch (System.Threading.ThreadAbortException)
                {
                    return;
                }
                catch (Exception ex)
                {
                    switch (MessageBox.Show("Tentar novamente?\n" + ex.GetType() + '\n' + ex.Message + '\n' + ex.StackTrace, "Exceção não tratada", MessageBoxButtons.RetryCancel, MessageBoxIcon.Stop))
                    {
                        case DialogResult.Cancel:
                            PararExecucao = true;
                            new System.Threading.Thread(new System.Threading.ThreadStart(() =>
                            {
                                if (dgvInstancias.SelectedRows[0].Index == dgvInstancias.Rows.Count - 1)
                                    bIniciarParar_Click(null, null);
                                else
                                    bProximo_Click(null, null);
                            })).Start();
                            break;
                        case DialogResult.Retry:
                            break;
                    }
                }
                finally
                {
                    if (nIteracoes.Value > 0 && iteracao > nIteracoes.Value)
                    {
                        //MessageBox.Show(string.Format("si:{0}\nrot:{1}\nref:{2}", totalSi / iteracao, totalRot / iteracao, totalRef / iteracao));
                        PararExecucao = true;
                        new System.Threading.Thread(new System.Threading.ThreadStart(() =>
                        {
                            if (dgvInstancias.SelectedRows[0].Index == dgvInstancias.Rows.Count - 1)
                                bIniciarParar_Click(null, null);
                            else
                                bProximo_Click(null, null);
                        })).Start();
                    }
                }
            }
        }

        [PreEmptive.Attributes.Feature("CompleteDesignationRoutingRefining")]
        private void ArmazenaResultado(IdInstancia id, Resultados resultado, int iteracao)
        {
            cEvolucao.Invoke(new MethodInvoker(() =>
                {
                    cEvolucao.Series["VMP"].Points.AddXY(iteracao, resultado.vmpCusto);
                    cEvolucao.Series["IME"].Points.AddXY(iteracao, resultado.imeCusto);
                }));
            if (!resultados.ContainsKey(id) || new double[]{resultados[id].vmpCusto, resultados[id].imeCusto}.Min() >= new double[]{resultado.vmpCusto, resultado.imeCusto}.Min())
            {
                if (resultados.ContainsKey(id) && resultados[id].TempoProcessamento <= resultado.TempoProcessamento)//mantém sempre o menor tempo de processamento
                    resultado.TempoProcessamento = resultados[id].TempoProcessamento;
                resultados[id] = resultado;
                dgvResultados.BeginInvoke(new MethodInvoker(() =>
                {
                    AtualizaDgvResultados();
                    dgvResultados.CurrentCell = dgvResultados.Rows.OfType<DataGridViewRow>().AsQueryable()
                        .Where(r => ((string)r.Cells[0].Value) == id.Classe && ((string)r.Cells[1].Value) == id.Instancia).First().Cells[0];
                }));
                cEvolucao.Invoke(new MethodInvoker(() =>
                {
                    cEvolucao.Series["Ótimo"].Points.AddXY(iteracao, new double[]{resultado.imeCusto, resultado.vmpCusto}.Min());
                }));
            }
        }

        [PreEmptive.Attributes.Teardown]
        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            this.RodaInstancia.Abort();
        }

        private void bAnterior_Click(object sender, EventArgs e)
        {
            TrocaInstancia(-1);
        }

        private void bProximo_Click(object sender, EventArgs e)
        {
            TrocaInstancia(1);
        }

        private void TrocaInstancia(int p)
        {
            if (dgvInstancias.SelectedRows.Count != 0 && dgvInstancias.SelectedRows[0].Index + p >= 0 && dgvInstancias.SelectedRows[0].Index < dgvInstancias.Rows.Count - 1)
            {
                dgvInstancias.Invoke(new MethodInvoker(() =>
                {
                    dgvInstancias.CurrentCell = dgvInstancias.Rows[dgvInstancias.SelectedRows[0].Index + p].Cells[0];
                }));
            }
        }

        private void dgvInstancias_RowEnter(object sender, DataGridViewCellEventArgs e)
        {
            if (RodaInstancia.ThreadState == System.Threading.ThreadState.Running || RodaInstancia.ThreadState == System.Threading.ThreadState.WaitSleepJoin)
                RodaInstancia.Abort();
            RodaInstancia = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(RotinaInstancia));
            if (dgvInstancias.SelectedRows.Count > 0 && bIniciarParar.Text == "Pa&rar")
                RodaInstancia.Start(new IdInstancia(dgvInstancias.SelectedRows[0].Cells[0].Value.ToString(), dgvInstancias.SelectedRows[0].Cells[1].Value.ToString()));
        }

        private void salvarResultadosToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (bIniciarParar.Text == "Parar")
                bIniciarParar_Click(sender, e);
            switch (SalvarResultados.ShowDialog())
            {
                case DialogResult.Abort:
                    break;
                case DialogResult.Cancel:
                    break;
                case DialogResult.Ignore:
                    break;
                case DialogResult.No:
                    break;
                case DialogResult.None:
                    break;
                case DialogResult.Retry:
                    break;
                case DialogResult.OK:
                case DialogResult.Yes:
                    var res = resultados.Values.ToArray();
                    var xs = new System.Xml.Serialization.XmlSerializer(res.GetType());
                    using (var xw = System.Xml.XmlWriter.Create(SalvarResultados.FileName))
                    {
                        xs.Serialize(xw, res);
                        xw.Flush();
                    }
                    break;
                default:
                    break;
            }
        }

        private void dgvResultados_CellMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
        {
            var id = new IdInstancia(dgvResultados.SelectedRows[0].Cells[0].Value.ToString(), dgvResultados.SelectedRows[0].Cells[1].Value.ToString());
            //new Mapa(resultados[id].Refino, id).Show();
        }

        private void abrirResultadosToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (bIniciarParar.Text == "Pa&rar")
                bIniciarParar_Click(sender, e);
            switch (ResultadosDataset.ShowDialog())
            {
                case DialogResult.Abort:
                    break;
                case DialogResult.Cancel:
                    break;
                case DialogResult.Ignore:
                    break;
                case DialogResult.No:
                    break;
                case DialogResult.None:
                    break;
                case DialogResult.Retry:
                    break;
                case DialogResult.OK:
                case DialogResult.Yes:
                    var xs = new System.Xml.Serialization.XmlSerializer(typeof(Resultados[]));
                    using (var xr = System.Xml.XmlReader.Create(ResultadosDataset.FileName))
                    {
                        resultados = ((Resultados[])xs.Deserialize(xr)).ToDictionary(d => d.Id, d => d);
                    }
                    AtualizaDgvResultados();
                    break;
                default:
                    break;
            }
        }

        private void AtualizaDgvResultados()
        {
            dgvResultados.DataSource = null;
            dgvResultados.DataSource = resultados.Select(s =>
            {
                return new
                {
                    s.Key.Classe,
                    s.Key.Instancia,
                    s.Value.vmpCusto,
                    s.Value.imeCusto,
                    Duração = s.Value.TempoProcessamento,
                    s.Value.Desvio
                };
            }).OrderBy(o => o.Classe).ThenBy(o => o.Instancia).ToList();
            dgvResultados.Columns["Desvio"].DefaultCellStyle.Format = "F";
            dgvResultados.Columns["Duração"].DefaultCellStyle.Format = "F";
            dgvResultados.Columns["vmpCusto"].DefaultCellStyle.Format = "F";
            dgvResultados.Columns["imeCusto"].DefaultCellStyle.Format = "F";
        }

        private void sairToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        [PreEmptive.Attributes.Setup(CustomEndpoint = "so-s.info/PreEmptive.Web.Services.Messaging/MessagingServiceV2.asmx")]
        private void Form1_Load(object sender, EventArgs e)
        {
        }

        private void rotasEmFormaDeTabelaToolStripMenuItem_Click(object sender, EventArgs e)
        {
            new VisualizadorTabela.VizualizarResultadosTabela(resultados.Values.ToArray()).Show();
        }

        private void abrirInstânciaEmExcelParaGLPToolStripMenuItem_Click(object sender, EventArgs e)
        {
            switch (ExcelGlpFileDialog.ShowDialog())
            {
                case DialogResult.Abort:
                    break;
                case DialogResult.Cancel:
                    break;
                case DialogResult.Ignore:
                    break;
                case DialogResult.No:
                    break;
                case DialogResult.None:
                    break;
                case DialogResult.OK:
                case DialogResult.Yes:
                    new System.Threading.Thread(() =>
                    {
                        var r = new DetalhesInstanciaGlp().ShowDialog();
                        if (r == System.Windows.Forms.DialogResult.Cancel)
                            return;
                        var inst = new InstanciaExcelGlp(ExcelGlpFileDialog.FileName);
                        inst.QuantidadeVeiculos = int.Parse(Properties.Settings.Default.Quantidade);
                        inst.CapacidadeVeiculo = int.Parse(Properties.Settings.Default.Capacidade);
                        inst.Deposito = new BaseClasses.Point()
                        {
                            X = double.Parse(Properties.Settings.Default.Longitude),
                            Y = double.Parse(Properties.Settings.Default.Latitude)
                        };
                        Instancias = new InstanciaDataset(new IdInstancia(Properties.Settings.Default.Classe, Properties.Settings.Default.Instancia), inst);
                        PopuladgvInstancias();
                    }).Start();
                    break;
                case DialogResult.Retry:
                    break;
                default:
                    break;
            }
        }
    }

    public class Resultados
    {
        public IdInstancia Id { get; set; }

        public double vmpCusto { get; set; }

        public double imeCusto { get; set; }

        public double TempoProcessamento { get; set; }

        public double Desvio { get; set; }

        public double Benchmark { get; set; }

        public LGPeWJNM.BaseClasses.Cliente[][] SolucaoInicial { get; set; }

        public LGPeWJNM.BaseClasses.Cliente[][] vmpRoteirizacao { get; set; }

        public LGPeWJNM.BaseClasses.Cliente[][] imeRoteirizacao { get; set; }

        public LGPeWJNM.BaseClasses.Cliente[][] vmpRefino { get; set; }

        public LGPeWJNM.BaseClasses.Cliente[][] imeRefino { get; set; }
    }
}